Web Cryptoで楕円曲線ディフィー・ヘルマン鍵共有して、暗号化 & 復号
window.cryptoを使ってブラウザで鍵の生成、共有・暗号化をするときに使える例。 以下のrawとdecryptedが同じ[1, 2, 3]になることが確認できればOK。厳密には型がUint8ArrayとArrayBufferで違うが、合わせたくなったときに簡単に合わせられるのでここでは簡単のために触れない。
途中に出てくるaPublicKeyJWKやbPublicKeyJWKはJSONで、JSON.stringify()すればネットワーク経由で渡せる。bRemotePublicKeyはネットワーク経由で渡ってきたものを使ってると解釈すればOK。
code:ts
(async ()=>{
const aKeyPair: CryptoKeyPair = await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256'},
false,
);
const aPublicKeyJWK = await crypto.subtle.exportKey('jwk', aKeyPair.publicKey);
console.log(aPublicKeyJWK);
console.log(JSON.stringify(aPublicKeyJWK, null, " "));
const bKeyPair: CryptoKeyPair = await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256'},
false,
);
const bPublicKeyJWK = await crypto.subtle.exportKey('jwk', bKeyPair.publicKey);
const bRemotePublicKey = await crypto.subtle.importKey(
'jwk',
bPublicKeyJWK,
{name: 'ECDH', namedCurve: 'P-256'},
false,
[]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const aKey = await crypto.subtle.deriveKey(
{ name: 'ECDH', public: bRemotePublicKey },
aKeyPair.privateKey,
{'name': 'AES-GCM', length: 128},
false,
);
const raw = new Uint8Array(1, 2, 3); const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
aKey,
raw
);
console.log("encrypted:", encrypted);
const aRemotePublicKey = await crypto.subtle.importKey(
'jwk',
aPublicKeyJWK,
{ name: 'ECDH', namedCurve: 'P-256'},
false,
[]
);
const bKey = await crypto.subtle.deriveKey(
{ name: 'ECDH', public: aRemotePublicKey },
bKeyPair.privateKey,
{ name: 'AES-GCM', length: 128 },
false,
);
console.log('aKey:', aKey);
console.log('bKey:', bKey);
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
bKey,
encrypted
);
console.log('raw:', raw);
console.log('decrypted:', decrypted);
})();